#!/usr/bin/env python3
# 导入必要的库
import cv2          # OpenCV库，用于图像处理
import sys          # 系统库，用于处理命令行参数
import os           # 操作系统库，用于文件路径操作
from datetime import datetime  # 时间库，用于计算处理时间

# 调试模式开关
DEBUG = False
# DEBUG = True

# 目标图像尺寸定义(SSD1306 OLED屏幕分辨率)
TARGET_WIDTH = 128    # OLED屏幕宽度(像素)
TARGET_HEIGHT = 64    # OLED屏幕高度(像素)
PIXEL_PER_BYTE = 8    # 每个字节表示的像素数(8位=1字节)
WIDTH_BYTES = int(TARGET_WIDTH/PIXEL_PER_BYTE)  # 每行需要的字节数
PIXEL_THRESHOUD = 128.0  # 二值化阈值(0-255)

# 将多个灰度像素打包到一个字节中
def pack_pixels(pixels):
    """
    将8个连续像素值打包为1个字节
    参数: pixels - 包含8个灰度值的列表/数组
    返回: 打包后的字节值
    """
    value = 0
    for gray in pixels:
        bit = 1 if gray >= PIXEL_THRESHOUD else 0  # 二值化处理
        value = (value << 1) + bit  # 左移并添加新位
    return value

frameCount = 0  # 调试用帧计数器

def convert_frame_to_bytes(frame):
    """
    将图像帧转换为SSD1306兼容的字节数组
    参数: frame - 输入的BGR图像帧
    返回: 转换后的字节数组
    """
    data = []
    start = datetime.now()  # 记录开始时间
    
    # 图像预处理
    frame = cv2.resize(frame, (TARGET_WIDTH, TARGET_HEIGHT))  # 调整图像尺寸
    frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)  # 转换为灰度图像

    # 逐行处理图像
    for r in range(TARGET_HEIGHT):
        for b in range(WIDTH_BYTES):
            colStart = b * PIXEL_PER_BYTE
            pixels = frame[r, colStart: colStart + PIXEL_PER_BYTE]
            byte = pack_pixels(pixels)  # 打包8个像素为1字节
            data.append(byte)
    
    # 调试信息输出
    if DEBUG:
        global frameCount
        cv2.imwrite(os.path.join('debug', str(frameCount) + '.png'), frame)
        frameCount += 1
        end = datetime.now()
        print('time cost:', end - start)
    
    return bytes(data)  # 返回字节数组

def main():
    """
    主函数：处理命令行参数并执行图像转换
    """
    # 检查帮助参数
    if len(sys.argv) == 2 and sys.argv[1] in ('-h', '--help'):
        print("""
SSD1306图像转换工具 - 将图片转换为OLED屏幕可显示的C数组格式

使用方法:
  python img2code.py [选项] <输入图片> <输出目录>

选项:
  -h, --help    显示帮助信息

参数:
  输入图片      要转换的图片文件路径(如: logo.png)
  输出目录      生成的C文件存放目录(如: ./output)

示例:
  python img2code.py logo.png ./output
  python img2code.py -h

功能说明:
  1. 自动将图片调整为128x64像素(SSD1306标准分辨率)
  2. 转换为灰度图并进行二值化处理
  3. 每个字节代表8个连续像素(1位=1像素)
  4. 生成可直接在SSD1306 OLED屏幕上显示的C语言数组
""")
        exit(0)

    # 检查命令行参数
    if len(sys.argv) < 3:
        print("Usage: {} input outdir\n使用 -h 或 --help 查看详细帮助".format(sys.argv[0]))
        exit(-1)

    # 获取输入输出路径
    imgPath = sys.argv[1]  # 输入图像路径
    outdir = sys.argv[2]   # 输出目录
    imgFile = os.path.split(imgPath)[-1]  # 提取文件名
    imgBase = imgFile.split('.')[-2]      # 提取不带扩展名的文件名
    codeFile = os.path.join(outdir, imgBase + '.c')  # 输出C文件路径
    
    # 创建输出目录(如果不存在)
    if not os.path.exists(outdir):
        os.mkdir(outdir)

    # 读取并转换图像
    frame = cv2.imread(imgPath)  # 读取图像
    bitmap = convert_frame_to_bytes(frame)  # 转换为字节数组

    # 写入C源文件
    with open(codeFile, 'w+') as f:
        # 写入数组声明
        f.write('const unsigned char ' + imgBase + '[] = {\n')
        
        # 写入字节数据(每行16字节)
        for i in range(len(bitmap)):
            v = bitmap[i]
            sep = '\n' if (i+1) % (TARGET_WIDTH/PIXEL_PER_BYTE) == 0 else ' '
            f.write('0x%02X,%s' % (v, sep))
        
        f.write('};\n')  # 结束数组
    
    print(imgFile, '=>', codeFile, 'done!')  # 输出完成信息

if __name__ == "__main__":
    main()  # 程序入口
